import requests
from bs4 import BeautifulSoup
import logging
import pandas as pd
import hashlib
import time

logging.basicConfig(
    level=logging.WARNING,  # 设置日志级别为 INFO（会输出 ERROR 及以上级别）
    format="%(asctime)s - %(levelname)s - %(message)s",
    handlers=[logging.StreamHandler()]  # 输出到控制台
)

# 定义一个类来处理音乐信息的抓取


class MusicScraper:
    def __init__(self, singer,appid,secret,timestamp):
        self.singer = singer
        self.url = f'https://music.91q.com/search?word={singer}'
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"
        }
        self.appid = appid
        self.secret = secret
        self.timestamp = timestamp

    def fetch_music_info(self):
        try:
            response = requests.get(self.url, headers=self.headers)
            if response.history != []:   # history 会记录重定向过程的信息
                logging.info(f'Sites redirected: {response.history}')
            if response.status_code != 200:
                logging.error(
                    f'Requests Error: {response.status_code}, {response.reason}')
            response.raise_for_status()
        except requests.exceptions.RequestException as e:
            logging.error(f"Error in fetching music info: {e}")
            return None

        soup = BeautifulSoup(response.text, 'html.parser')
        song_box = soup.find_all(name='div', class_='song-box')
        singer_box = soup.find_all(name='div', class_='artist ellipsis')

        music_info = []
        for song, singer_list in zip(song_box, singer_box):
            musicname = song.find('a').text
            song_id = song.find('a').get('href')
            musicid = "https://music.91q.com" + song_id
            T_idx = song_id.find('T')
            TSID = song_id[T_idx:]
            i = singer_list.find_all(name='a')
            singer = ", ".join(a.get_text(strip=True) for a in i)
            music_info.append(
                {'musicname': musicname, 'musicid': musicid, 'singer': singer, 'TSID': TSID})

        return pd.DataFrame(music_info)
     
# 以上可以输出歌手和其歌曲信息，还需要做到以下内容：
# 歌曲mp3--url、下载的sign密钥、翻页收集并下载、存储
## MP3 url：客户端先请求API接口 /v1/song/tracklink----API返回的URL可以直接获取音频文件
## 需要先创建sign字符串，然后拼接tracklink
    @staticmethod
    def createSign(secret, appid, TSID, timestamp):
 
        # r='TSID=T10063954909,T10061819807,T10059607241,T10064980207,T10064970806&appid=16073360&timestamp=17421293440b50b02fd0d73a9c4c8c3a781c30845f'
        r = f'TSID={TSID}&appid={self.appid}&timestamp={self.timestamp}{self.secret}'
        return hashlib.md5(r.encode('utf-8')).hexdigest()
        # 首先， r.encode('utf-8') 将字符串 r 转换为字节码
        # 然后， hashlib.md5() 对这些字节码进行 MD5 加密，生成一个哈希对象
        # 最后， hexdigest() 将这个哈希对象转换为十六进制字符串 (源代码的使用需求)

# 创建sign密钥 
# 为所有歌曲生成签名
## 使用 apply 可以清晰地表达“对每一行应用某个函数”的意图。
## df['TSID'] 获取 DataFrame 中名为 TSID 的列，返回一个 Pandas Series 对象
## .apply()  将指定的函数应用到 Series 的每个元素上
## lambda 匿名函数，定义函数; tsid 是定义的参数并传入createsign

    df['sign'] = df['TSID'].apply(lambda tsid: createSign(TSID=tsid, timestamp=timestamp))

    def get_mp3_url(self, TSID):
        timestamp = int(time.time())  # 确保每次调用都生成新的时间戳
        params = {
            "appid": self.appid,  # appid 是固定的，可以直接使用
            "timestamp": self.timestamp,
            "sign": self.createSign(TSID=TSID, timestamp=timestamp),
            "TSID": TSID
        }
        try:
            response = requests.get("https://music.91q.com/v1/song/tracklink", params=params)
            response.raise_for_status()
            data = response.json()
            mp3_url = data.get('data', {}).get('path')
            if not mp3_url:
                logging.warning(f"No MP3 URL found for TSID: {TSID}")
            return mp3_url
        except requests.exceptions.RequestException as e:
            logging.error(f"Error fetching MP3 URL for TSID {TSID}: {e}")
            return None
        except ValueError as e:
            logging.error(f"Error parsing JSON response for TSID {TSID}: {e}")
            return None


if __name__ == "__main__":
    logging.info("MusicScraper is running.")
# 示例调用
    appid = "16073360"
    secret = "0b50b02fd0d73a9c4c8c3a781c30845f"
    ## 在调用 createSign 时生成时间戳，使用的是秒级
    timestamp = int(time.time())  # 秒级的时间戳、10位；毫秒级时间戳 （13位整数）
    singer = input("请输入歌手名：")
    scraper = MusicScraper(singer)
    df = scraper.fetch_music_info()





